package com.asen.commons.core.jdbc.handler;

import com.asen.commons.core.meta.entity.EntityObject;
import com.asen.commons.core.meta.entity.FieldObject;
import com.asen.commons.core.meta.loader.EntityObjectLoader;
import com.asen.commons.core.utils.EntityUtils;
import com.asen.commons.utils.BaseAssert;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Types;

/**
 * 处理model值对象类型
 *
 * @author Gary
 * @version 1.0
 * @date 2018-11-29
 */
public class ModelTypeHandler<T> extends BaseTypeHandler<T> {

    private EntityObjectLoader loader = EntityObjectLoader.getInstance();

    private Class<T> type;

    public ModelTypeHandler() {
    }

    public ModelTypeHandler(Class<T> type) {
        BaseAssert.notNull(type, "参数type不能为空");
        this.type = type;
    }

    @Override
    public int getSqlType() {
        return Types.BIGINT;
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, T parameter)
            throws Exception {
        EntityObject eo = null;
        if (type != null) {
            eo = loader.get(this.type);
        } else {
            eo = loader.get(parameter.getClass());
        }
        Long id = (Long) eo.getFieldValue(parameter, "id");
        BaseAssert.notNull(id, "不支持的参数 #" + i + " 类型【" + parameter.getClass().getName() + "】");
        ps.setLong(i, id);
    }

    @Override
    public T getNullableResult(ResultSet rs, String columnName)
            throws Exception {
        return convert(rs.getLong(columnName));
    }

    @Override
    public T getNullableResult(ResultSet rs, int columnIndex)
            throws Exception {
        return convert(rs.getLong(columnIndex));
    }

    @Override
    public T getNullableResult(CallableStatement cs, int columnIndex)
            throws Exception {
        return convert(cs.getLong(columnIndex));
    }

    private T convert(Long id) throws Exception {
        Class<T> modelClazz = null;
        if (this.type != null) {
            modelClazz = this.type;
        } else {
            modelClazz = getRefRawType();
        }

        T model = modelClazz.newInstance();
        EntityObject eo = loader.get(modelClazz);
        FieldObject fo = eo.getFieldObject("id");
        BaseAssert.notNull(fo, "不支持的没有ID属性的类型【" + modelClazz.getClass().getName() + "】");
        eo.setFieldValue(model, "id", id);
        return model;
    }

    @Override
    public String toSqlValue(Object value) throws Exception {
        if (value == null) {
            return null;
        }

        if (value instanceof Long) {
            return String.valueOf(value);
        }

        if (value instanceof String) {
            return String.valueOf(value);
        }

        if (value instanceof String[]) {
            return getSqlValue((String[]) value, false);
        }

        if (value instanceof Long[]) {
            return getSqlValue((String[]) value, false);
        }

        Class<?> clazz = type;

        if (type == null) {
            clazz = value.getClass();
        }

        EntityObject eo = loader.get(clazz);

        BaseAssert.notNull(eo.hasField("id"), "不支持没有ID属性的Model【" + clazz.getName() + "】");

        Long id = (Long) eo.getFieldValue(value, "id");
        if (id == null || id == 0) {
            return null;
        }

        return String.valueOf(id);
    }

    @Override
    public T getValue(Object value) throws Exception {
        if (value == null) {
            return null;
        }

        if (type.isAssignableFrom(value.getClass())) {
            return (T) value;
        }

        if (Long.class.isAssignableFrom(value.getClass())) {
            EntityObject eo = loader.get(type);
            if (eo.hasField("id")) {
                T obj = this.type.newInstance();
                eo.setFieldValue(obj, "id", (Long) value);
            }
            return null;
        }

        if (!EntityUtils.isSimpleType(value.getClass())) {
            T obj = this.type.newInstance();
            EntityUtils.copyProperties(value, obj);
            return obj;
        }

        return null;
    }
}
